/*
 * PgAbm.java
 *
 */
package dbUtil;

import java.sql.*;
import java.util.*;
import utils.MD5;

import javax.servlet.http.*;

/**
 *
 * @author marcos
 */
public class PgAbm {
    // Valores del objeto
    private Hashtable hash_campos;
    int cant_filas;    // Result set de los datos
    private ResultSet rs_datos;    // Result set de valores autogenerados
    private ResultSet rs_auto;    // Array conteniendo el nombre de los campos
    private ArrayList array_campos;    // Array conteniendo el nombre de las claves    
    private ArrayList array_claves;    // Array conteniendo el nombre de las claves    
    private String[] array_autogenkeys;    // Establece los campos de tipo serial y las correspondiente secuencias asociadas
    private Hashtable hash_seq;    // Variable de Conecci�n a la base de datos
    protected Connection con;    // Nombre de la tabla a consultar
    private String table_name;    // Modo en que se realizar� la b�squeda
    private int search_mode;    // Permite establecer el orden en que se recuperaran los datos
    // Guarda orden en que se desplegaran los campos
    private ArrayList array_orden_campos;
    private ArrayList array_orden_values;    // Lista de modos de b�squeda
    public static int SEARCH_APROX = 0;
    public static int SEARCH_EXACT = 1;    // Modo de sensibilidad con la que se realizar� la b�squeda
    private int sensitive_mode;    // Lista de modos de Alcace/Sensibilidad de b�squeda
    public static int SEARCH_ALL = 5;
    public static int SEARCH_SOME = 6;
    public static int ORD_ASC = 0;
    public static int ORD_DES = 1;
    private int limit;
    private int offset;

    public PgAbm() {
        search_mode = this.SEARCH_EXACT;
        sensitive_mode = this.SEARCH_ALL;
        limit = -1;
        offset = -1;
    }

    public PgAbm(ArrayList array_campos, String tabla, ArrayList array_claves) {
        this.array_campos = array_campos;
        this.table_name = tabla;
        this.array_claves = array_claves;

    }

    public void setSequence(String campo, String sequence) {
        hash_seq.put(campo, sequence);
    }

    public void setCon(Connection con) {
        this.con = con;
    }

    /** Inicializa las estructuras internas a utilizarse.*/
    public void initMapping() {
        hash_campos = new Hashtable();
        array_orden_campos = new ArrayList();
        array_orden_values = new ArrayList();
        hash_seq = new Hashtable();
    }

    public void setMapValue(Object key, Object value) {
        hash_campos.put(key, value);
    }

    public Object getMapValue(Object key) {
        return hash_campos.get(key);
    }

    public void removeMapValue(String key) {
        hash_campos.remove(key);
    }

    public void setTableName(String table_name) {
        this.table_name = table_name;
    }

    public void setClaves(ArrayList array_claves) {
        this.array_claves = array_claves;
    }

    public void setCampos(ArrayList array_campos) {
        this.array_campos = array_campos;
    }

    public void setAutoGenkeys(ArrayList array_auto) {
        array_autogenkeys = new String[array_auto.size()];
        for (int i = 0; i < array_auto.size(); i++) {
            array_autogenkeys[i] = (String) array_auto.get(i);
        }
    }

    public ArrayList getClaves() {
        return this.array_claves;
    }

    public ArrayList getCampos() {
        return this.array_campos;
    }

    /** Realiza la consulta a la base de datos, recuperando los datos
     */
    public int load_data() throws Exception {
        String sql_query = "SELECT ";
        // Concatena los campos de la tabla, separandolo entre comas.
        // Realiza la proyecci�n de los campos.

        ListIterator iter_campos = array_campos.listIterator();
        String campos = "";
        String coma = "";

        while (iter_campos.hasNext()) {
            String value = (String) iter_campos.next();
            campos += coma + value;
            coma = ",";
        }

        sql_query += campos + " FROM " + table_name;

        //Vemos si hay algun dato completado de manera a realizar un filtrado
        //Para esto se genera la parte del "WHERE" del sql_query
        String whereparams = "";

        int op_bool = -1;
        if (sensitive_mode == SEARCH_ALL) {
            op_bool = BD.AND_op;
        } else if (sensitive_mode == SEARCH_SOME) {
            op_bool = BD.OR_op;
        }

        if (search_mode == SEARCH_EXACT) {
            whereparams = BD.createWhereStm(hash_campos, op_bool);
        } else if (search_mode == SEARCH_APROX) {
            whereparams = BD.createWhereLikeStm(hash_campos, op_bool);
        }

        // Se concatena el WHERE al sql_query
        if (whereparams.length() > 0) {
            sql_query += " WHERE " + whereparams;
        }
        String str_orderby = "";

        if (array_orden_campos != null) {

            coma = "";
            for (int i = 0; i < array_orden_campos.size(); i++) {
                str_orderby += coma + (String) array_orden_campos.get(i) + " " +
                        (String) array_orden_values.get(i);
                coma = " , ";
            }
        }

        if (str_orderby.length() > 0) {
            sql_query += " ORDER BY " + str_orderby;
        }


        sql_query += getLimitOffset();

        // Generamos un Statement, configurando los par�metros como para poder 
        // recuperar un ResultSet Scrollable. Esto es necesario para poder des-
        // plazarse en ambas direcciones forward y reverse. 

        Statement stm = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
                ResultSet.CONCUR_READ_ONLY);

        // Se ejecuta la consulta y se recupera el ResultSet        
        System.out.println("SQL: " + sql_query);
        try{
        rs_datos = stm.executeQuery(sql_query);
        }catch (SQLException ex){
            System.out.println(ex.getSQLState());
            System.out.println(ex.getMessage());
        }

        // Se recupera el n�mero de registros recuperados.
        // No hay una raz�n tecnica para hacerlo de esta manera, 
        // pero es la manera hist�rica en la que lo vengo haciendo :)

        cant_filas = 0;
        if (rs_datos.last()) {
            cant_filas = rs_datos.getRow();
            rs_datos.beforeFirst();
        }
        return cant_filas;
    }

    /** Eliminar datos de la base de datos
     */
    public boolean remove_data() throws Exception {
        String sql_query = "DELETE FROM " + table_name;

        // Creamos la parte del where del sql_query        
        String whereparams = BD.createWhereStm(hash_campos, BD.AND_op);

        if (whereparams.length() > 0) {
            sql_query += " WHERE " + whereparams;
        }

        PreparedStatement ps = con.prepareStatement(sql_query);
        System.out.println("SQL: " + sql_query);
        ps.execute();
        return true;
    }

    /** Guarda los datos
     */
    public boolean save_data() throws Exception {
        this.loadAutoGenKeys();

        ListIterator iter_campos = array_campos.listIterator();
        String campos = "";
        String coma = "";
        String valores = "";
        while (iter_campos.hasNext()) {
            String key = (String) iter_campos.next();


            Object valor = hash_campos.get(key);
            String str_valor = null;

            if ((valor != null) && (valor instanceof String)) {
                str_valor = "'" + (String) valor + "'";
            } else if (valor != null) {
                str_valor = "'" + valor.toString() + "'";
            }

            if (valor != null) {
                campos += coma + key;
                valores += coma + str_valor;
                coma = ",";
            }
        }

        String sql_query = "insert into " + table_name + "(" + campos + ") values (" + valores + ")";

        PreparedStatement ps = con.prepareStatement(sql_query);
        System.out.println("SQL: " + sql_query);
        ps.execute();

        return true;

    }

    /** Actualiza los datos
     *
     */
    public int update_data() throws Exception {

        Hashtable hash_tmp = (Hashtable) hash_campos.clone();
        Hashtable hash_claves = new Hashtable();

        for (int i = 0; i < array_claves.size(); i++) {
            String key = (String) array_claves.get(i);
            String value = (String) hash_tmp.get(key);
            if (value != null) {
                hash_claves.put(key, value);
                hash_tmp.remove(key);
            }
        }

        String set_stm = BD.createSetStm(hash_tmp);
        String where_stm = BD.createWhereStm(hash_claves, BD.AND_op);

        if ((set_stm.length() > 0) /*&& (where_stm.length() > 0) */) {
            String sql_query = "UPDATE " + table_name + " SET " + set_stm;
            if (where_stm.length() > 0) {
                sql_query += " WHERE " + where_stm;
            }

            PreparedStatement ps = con.prepareStatement(sql_query);
            System.out.println("SQL: " + sql_query);
            int num_update = ps.executeUpdate();

            return num_update;
        }
        return -1;
    }

    public Connection getConection() throws Exception {
        return con;
    }

    private boolean claves_incompletas() throws Exception {
        ListIterator iter = array_claves.listIterator();
        while (iter.hasNext()) {
            String key = (String) iter.next();
            Object valor = hash_campos.get(key);
            if (valor == null) {
                return true;
            }
        }
        return false;
    }

    /** Permite establecer el orden en que los campos de la tabla van a ser
     *  recuperados. 
     *  Construye un componente del tipo: <br>
     *  ORDER BY <campo1> <modo1> , <campo1> <modo2> ..<br>
     *  El orden de los campos a ser ordenados, depende del orden en que se 
     *  llama a setOrderBy. <br>
    
     *  @param campo : Campo a ser ordenado <br>
     *  @param modo  : Puede ser ORD_ASC o ORD_DES, dependiendo de si se desea ordenar
     *          de manera descendiente o ascendiente.
     *  @throws
     *    Modo de ordenaci�n no v�lido.
     */
    public void setOrderBy(String campo, int mode) throws Exception {
        if (mode == ORD_ASC) {
            this.array_orden_campos.add(campo);
            this.array_orden_values.add("ASC");
        } else if (mode == ORD_DES) {

            this.array_orden_campos.add(campo);
            this.array_orden_values.add("DESC");
        } else {
            throw new Exception("Orden de campo no v�lido");
        }

    }
    /* Retorna el parametro necesario para los campos */

    public String getOrderbyParameters(String prefix) {

        String str_orderby = "";
        String coma;
        if (prefix != null) {
            prefix += ".";
        } else {
            prefix = "";
        }

        if (array_orden_campos != null) {

            coma = "";
            for (int i = 0; i < array_orden_campos.size(); i++) {
                str_orderby += coma + prefix + (String) array_orden_campos.get(i) + " " +
                        (String) array_orden_values.get(i);
                coma = " , ";
            }
        }
        return str_orderby;
    }

    public String getSelectParameters(String prefix) {
        String str_campos = "";
        String coma = "";
        if (prefix != null) {
            prefix += ".";
        } else {
            prefix = "";
        }

        for (int i = 0; i < array_campos.size(); i++) {
            str_campos += coma + prefix + array_campos.get(i);
            coma = ",";
        }
        return str_campos;
    }

    protected ResultSet getResultSet() {
        return rs_datos;
    }

    public void loadAutoGenKeys() throws SQLException {
        Enumeration enum_keys = hash_seq.keys();

        while (enum_keys.hasMoreElements()) {
            String campo = (String) enum_keys.nextElement();
            nextSequence(campo);
        }
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public String getLimitOffset() {
        String str_lim_off = "";
        if (limit > 0) {
            str_lim_off = " limit " + limit;
        }
        if (offset > 0) {
            str_lim_off += " offset " + limit;
        }

        return str_lim_off;
    }

    protected void nextSequence(String campo) throws SQLException {
        String sql_query = " SELECT nextval('" + (String) hash_seq.get(campo) + "')";
        ResultSet rs = BD.ejecutarConsulta(con, sql_query);
        rs.next();
        String seqvalue = rs.getString("nextval");
        setMapValue(campo, seqvalue);
    }

    public String getAutoGenKey(String key) throws Exception {
        return rs_auto.getString(key);
    }

    /** Permite establecer el modo de b�squeda. Busqueda por aproximaciones
     *  o busqueda exacta. Aplicable a todos los par�metros establecidos con los
     *  operadores set*.
     *  SEARCH_APROX  : B�squeda por aproximaciones. Los campos ser�n construidos
     *                  con el operador LIKE en el filtro WHERE de la consulta, 
     *                  
     *  SEARCH_EXACT  : B�squeda exacta utilizando el operador de igualdad en la 
     *                  construcci�n del filtro de la consulta.
     */
    public void setSearchMode(int mode) throws Exception {
        if ((mode != SEARCH_APROX) && (mode != SEARCH_EXACT)) {
            throw new Exception("Modo de búsqueda inválido");
        }
        this.search_mode = mode;
    }

    /** Establece si es requisito encontrar todos los parametros
     *  establecidos en los metodos set* <br>
     *  Crea una clausula WHERE utilizando: <br>
     *  SEARCH_ALL  : Establece como union de expresiones el booleano AND.
     *                Todas los filtros deben ser cumplidos.
     *  SEARCH_SOME : Establece como union de expresiones el booleano OR.
     *                Al menos uno de los filtros debe ser cumplido.
     */
    public void setSearchScope(int mode) throws Exception {
        if ((mode != SEARCH_ALL) && (mode != SEARCH_SOME)) {
            throw new Exception("Modo de Sensibilidad de búsqueda inválido");
        }
        this.sensitive_mode = mode;
    }

    public String getTableName() {
        return table_name;
    }

    public String getWhereParamWithPrefix(String prefix, int op) {
        String stm = BD.createWhereStmWithPrefix(hash_campos, op, prefix);
        return stm;

    }

    public String getWhereLikeParamWithPrefix(String prefix, int op) {
        String stm = BD.createWhereLikeStmWithPrefix(hash_campos, op, prefix);
        return stm;
    }

    public String getWhereParam(int op) {
        String stm = BD.createWhereStm(hash_campos, op);
        return stm;
    }

    public String getWhereLikeParam(int op) {
        String stm = BD.createWhereLikeStm(hash_campos, op);
        return stm;
    }

    /** Recupera los valores del formulario contenidos en el request 
     *  y los asigna al Bean
     */
    public void setAllParams(HttpServletRequest request) {

        for (int i = 0; i < array_campos.size(); i++) {

            String campo = (String) array_campos.get(i);
            String valor = request.getParameter(campo);

            if ((valor != null) && (valor.trim().length() > 0)) {
                if (campo.equals("contrasenha") && valor.length() < 32) {
                    try {
                        MD5 pass = new MD5(valor);
                        valor = pass.getMD5();
                    } catch (Exception ex) {
                    }
                }
                this.setMapValue(campo, valor);
            }
        }

    }

    /** Autosetea los parametros provenientes de un result set que posee la estructura de la tabla
    a la cual representa el el bean*/
    public void setAllRsParams(ResultSet rs) throws SQLException {
        if (!rs.first()) {
            return;
        }
        for (int i = 0; i < array_campos.size(); i++) {
            String key = (String) array_campos.get(i);
            String value = (String) rs.getString(key);
            if (value != null) {
                setMapValue(key, value);
            }
        }
    }

    /** Autosetea los parametros provenientes recuperados mediante load_data */
    public void setAllRsParams() throws SQLException {
        setAllRsParams(rs_datos);
    }

    public int getSearchMode() {
        return search_mode;
    }

    public int getSearchScope() {
        return sensitive_mode;
    }

    public boolean tienePermiso(String servlet, String usuario, String operacion) throws Exception {
        String sql_query = "";

        sql_query = "select * " +
                " from rol_modulo_permiso rmp " +
                " join modulo_permiso mp on rmp.idmodulo = mp.idmodulo and rmp.idpermiso = mp.idpermiso " +
                " join rol r on rmp.idrol = r.idrol " +
                " join modulo m on m.idmodulo = rmp.idmodulo and m.pathservlet = '" + servlet + "' " +
                " join permiso p on rmp.idpermiso = p.idpermiso and p.operacion = '" + operacion + "'" +
                " join usuario u on u.nombreusuario = '" + usuario + "' " +
                " join usuario_rol ur on u.idusuario = ur.idusuario and rmp.idrol = ur.idrol; ";

        Statement stm = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
                ResultSet.CONCUR_READ_ONLY);

        // Se ejecuta la consulta y se recupera el ResultSet        
        System.out.println("SQL: " + sql_query);
        rs_datos = stm.executeQuery(sql_query);

        int cant_filas = 0;
        if (rs_datos.last()) {
            cant_filas = rs_datos.getRow();
            rs_datos.beforeFirst();
        }
        if (cant_filas > 0) {
            return true;
        } else {
            return false;
        }
    }
}





